package br.iars.web.model.impl;

import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.apache.log4j.Logger;

import br.iars.web.exception.PersistenceException;
import br.iars.web.model.api.GenericModel;
import br.iars.web.model.entity.GenericEntity;

public abstract class GenericModelImpl<T extends GenericEntity<PK>, PK extends Comparable<PK>> implements GenericModel<T, PK> {

	private EntityManager entityManager;
	private transient Logger logger;
	private static final long serialVersionUID = 1L;	
	private Class<T> persistentClass;
	
	@SuppressWarnings("unchecked")
	public GenericModelImpl() {
		super();
		ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
		this.persistentClass = (Class<T>) p.getActualTypeArguments()[0];
		this.logger = Logger.getLogger(getClass());
		
		EntityManagerFactory factory = Persistence.createEntityManagerFactory("recommendation-samples-pu");
		entityManager =  factory.createEntityManager();
	}

	@SuppressWarnings("unchecked")
	public GenericModelImpl(EntityManager entityManager) {
		super();
		this.entityManager = entityManager;
		ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
		this.persistentClass = (Class<T>) p.getActualTypeArguments()[0];
		this.logger = Logger.getLogger(getClass());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.iars.web.model.impl.GenericModel#findAll()
	 */
	@SuppressWarnings("unchecked")
	public List<T> findAll() {
		List<T> result = null;
		try {
			result = entityManager.createQuery("SELECT e FROM " + getPersistentClass().getSimpleName() + " AS e").getResultList();
		} catch (RuntimeException e) {
			logger.error("Falha ao buscar lista da entidade: " + getPersistentClassName(), e);
			throw e;
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.iars.web.model.impl.GenericModel#findByID(PK)
	 */
	public T findByID(PK id) {
		T result = null;
		try {
			result = entityManager.find(getPersistentClass(), id);
		} catch (RuntimeException e) {
			logger.error("Falha ao buscar entidade: " + getPersistentClassName() + ", com o ID: " + id, e);
			throw e;
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.iars.web.model.impl.GenericModel#insert(T)
	 */
	public void insert(T entity) throws PersistenceException {
		try {
			entityManager.getTransaction().begin();
			entityManager.persist(entity);
			entityManager.getTransaction().commit();
		} catch (RuntimeException e) {
			getLogger().error("Falha ao inserir entidade: " + entity, e);
			throw e;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.iars.web.model.impl.GenericModel#update(T)
	 */
	public void update(T entity) throws PersistenceException {
		try {
			entityManager.getTransaction().begin();
			entityManager.merge(entity);
			entityManager.getTransaction().commit();
		} catch (RuntimeException e) {
			getLogger().error("Falha ao atualizar entidade: " + entity, e);
			throw e;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.iars.web.model.impl.GenericModel#remove(PK)
	 */
	public void remove(PK id) throws PersistenceException {
		T entity = findByID(id);
		if (entity != null) {
			try {
				entityManager.getTransaction().begin();
				entityManager.remove(entity);
				entityManager.getTransaction().commit();
			} catch (RuntimeException e) {
				getLogger().error("Falha ao remover entidade: " + entity, e);
				throw e;
			}
		} else {
			throw new PersistenceException("error.dao." + getPersistentClassName().toLowerCase() + ".ja.removida");
		}
	}

	protected EntityManager getEntityManager() {
		return entityManager;
	}

	protected String getPersistentClassName() {
		return getPersistentClass().getSimpleName();
	}

	protected Class<T> getPersistentClass() {
		return persistentClass;
	}

	protected Logger getLogger() {
		return logger;
	}

}
